package parse

import (
	"fmt"
	"os"
	"strconv"
	"strings"

	"gitee.com/krio/ginc/util"
	"github.com/emicklei/proto"
	"github.com/gookit/color"
)

// ParseProto 获取proto解析信息
func ParseProto(protoPaths []string) ([]*ParseProtoInfo, *Result, error) {
	color.Bluep("[ 解析： proto ]", "\n")
	var (
		protoInfoList []*ParseProtoInfo
		result        = &Result{}
	)
	for _, p := range protoPaths {
		res, err := parseProto(p)
		if err != nil {
			return nil, nil, err
		}
		protoInfoList = append(protoInfoList, res)
	}
	counterMap := make(map[string]struct{})
	for _, v := range protoInfoList {
		result.ServiceCount++
		for _, vv := range v.ApiList {
			result.ApiCount++
			Key := fmt.Sprintf("[%s]:%v", vv.Method, vv.URL)
			if _, exi := counterMap[Key]; exi {
				result.ErrList = append(result.ErrList, fmt.Sprintf("%s 重复定义", Key))
			}

			counterMap[Key] = struct{}{}
		}
	}

	return protoInfoList, result, nil
}

func FormatStringSliceToString(vals []string) string {
	str := "[]string{\n"
	for _, val := range vals {
		str += fmt.Sprintf("\"%s\",\n", val)
	}
	str += "}"
	return str
}

func FormatLimitStructToString(val LimitOption) string {
	str := `LimitOption{
	Type:         "%s",
	Mode:         "%s",
	Cap:          %d,
	IntervalTime: %d,
	ExpireTime:   %d,
},
`
	return fmt.Sprintf(str, val.Type, val.Mode, val.Cap, val.IntervalTime, val.ExpireTime)
}

// 解析proto文件内容
func parseProto(protoFilePath string) (*ParseProtoInfo, error) {
	reader, err := os.Open(protoFilePath)
	if err != nil {
		return nil, fmt.Errorf("读取proto文件错误: %s %+v", protoFilePath, err)
	}
	defer reader.Close()
	parser := proto.NewParser(reader)
	parseResult, err := parser.Parse()
	if err != nil {
		return nil, fmt.Errorf("解析proto内容错误: %s %+v", protoFilePath, err)
	}
	origProto := &OrigProtoInfo{
		FilePath: protoFilePath,
		Option:   make(map[string]string),
		Service:  nil,
		Message:  nil,
	}
	proto.Walk(parseResult, proto.WithService(func(val *proto.Service) {
		origProto.Service = append(origProto.Service, val)
	}))
	proto.Walk(parseResult, proto.WithOption(func(val *proto.Option) {
		origProto.Option[val.Name] = strings.Trim(val.Constant.Source, "\"'")
	}))
	proto.Walk(parseResult, proto.WithRPC(func(val *proto.RPC) {
		origProto.Rpc = append(origProto.Rpc, val)
	}))
	proto.Walk(parseResult, proto.WithImport(func(val *proto.Import) {
		origProto.Import = append(origProto.Import, val.Filename)
	}))
	result, err := origProto.Parse()
	if err != nil {
		return nil, err
	}

	return result, nil
}

func (op *OrigProtoInfo) Parse() (*ParseProtoInfo, error) {
	var (
		result = &ParseProtoInfo{
			ProtoFile: op.FilePath,
		}
		err error
	)
	result.Service, err = op.ParseService()
	if err != nil {
		return nil, fmt.Errorf("解析service失败: %s %+v", op.FilePath, err)
	}
	result.Option, err = op.ParseOption()
	if err != nil {
		return nil, fmt.Errorf("解析option失败: %s %+v", op.FilePath, err)
	}
	if len(op.Service) > 1 {
		if result.Option.GoPackage == "" {
			return nil, fmt.Errorf("%s未设置", goPackageOptKey)
		}
		if result.Option.ServiceNodeName == "" {
			return nil, fmt.Errorf("%s未设置", serviceNameOptKey)
		}
		if result.Option.HandlerFile == "" {
			return nil, fmt.Errorf("%s未设置", handlerOptKey)
		}
	}
	result.ApiList, err = op.ParseRpc(result.Service, result.Option)
	if err != nil {
		return nil, fmt.Errorf("解析rpc失败: %s %+v", op.FilePath, err)
	}

	return result, nil
}

// ParseService 解析proto的service信息
func (op *OrigProtoInfo) ParseService() (*ParseServiceInfo, error) {
	result := &ParseServiceInfo{}
	dataLen := len(op.Service)
	if dataLen <= 0 {
		return result, nil
	}
	if dataLen > 1 {
		return nil, fmt.Errorf("ginc不支持单文件多service: %s", op.FilePath)
	}
	result.Name = op.Service[0].Name
	return result, nil
}

// 自定义proto的option
const (
	goPackageOptKey   = "go_package"     // 包引用
	serviceNameOptKey = "(service_name)" // service描述
	handlerOptKey     = "(handler_file)" // handler文件地址
	roleOptKey        = "role"           // 权限组
	middlewareOptKey  = "middleware"     // 中间件
	outputOptKey      = "output"         // 接口出参
	limiterOptKey     = "limiter"        // 限流器
)

// ParseOption 解析proto的option信息
func (op *OrigProtoInfo) ParseOption() (*ParseOptionInfo, error) {
	info := &ParseOptionInfo{}
	for key, val := range op.Option {
		switch key {
		case goPackageOptKey:
			info.GoPackage = strings.TrimSpace(val)
		case serviceNameOptKey:
			info.ServiceNodeName = strings.TrimSpace(val)
		case handlerOptKey:
			info.HandlerFile = strings.Trim(strings.TrimSpace(val), "/")
		}
	}

	return info, nil
}

type RouteOption struct {
	Roles       []string
	Middlewares []string
	Output      string
	Limiter     LimitOption
}

// ParseRpc 解析proto的rpc信息
func (op *OrigProtoInfo) ParseRpc(service *ParseServiceInfo, option *ParseOptionInfo) ([]*ParseRpcInfo, error) {
	var list []*ParseRpcInfo
	for _, origRpc := range op.Rpc {
		var desc string
		if origRpc.Comment != nil {
			desc = strings.TrimSpace(origRpc.Comment.Lines[0])
		}
		routeOpt := &RouteOption{}
		for _, v := range origRpc.Options {
			for _, vv := range v.Constant.OrderedMap {
				switch vv.Name {
				case roleOptKey:
					for _, vvv := range vv.Literal.Array {
						routeOpt.Roles = append(routeOpt.Roles, vvv.Source)
					}
				case middlewareOptKey:
					for _, vvv := range vv.Literal.Array {
						routeOpt.Middlewares = append(routeOpt.Middlewares, vvv.Source)
					}
				case outputOptKey:
					routeOpt.Output = vv.Source
				case limiterOptKey:
					for _, vvv := range vv.Literal.OrderedMap {
						switch vvv.Name {
						case "limit_type":
							routeOpt.Limiter.Type = vvv.Source
						case "limit_mode":
							routeOpt.Limiter.Mode = vvv.Source
						case "limit_cap":
							routeOpt.Limiter.Cap = GetInt64(vvv.Source)
						case "interval_time":
							routeOpt.Limiter.IntervalTime = GetInt64(vvv.Source)
						case "expire_time":
							routeOpt.Limiter.ExpireTime = GetInt64(vvv.Source)
						}
					}
				}
			}
		}
		info := &ParseRpcInfo{
			ServiceName:      service.Name,
			ServiceDesc:      option.ServiceNodeName,
			Name:             origRpc.Name,
			Desc:             desc,
			Request:          &Message{Name: origRpc.RequestType},
			Response:         &Message{Name: origRpc.ReturnsType},
			Middlewares:      routeOpt.Middlewares,
			Roles:            routeOpt.Roles,
			Output:           routeOpt.Output,
			Limiter:          LimitOption{},
			MiddlewareString: FormatStringSliceToString(routeOpt.Middlewares),
			RoleString:       FormatStringSliceToString(routeOpt.Roles),
			LimiterString:    FormatLimitStructToString(routeOpt.Limiter),
		}
		for _, element := range origRpc.Elements {
			switch v := element.(type) {
			case *proto.Option:
				for _, vv := range v.Constant.OrderedMap {
					if vv.IsString && util.InStringArr([]string{"get", "post", "put", "patch", "delete"}, strings.ToLower(vv.Name)) {
						info.Method = strings.ToUpper(vv.Name)
						info.URL = vv.Source
					}
				}
			}
		}
		list = append(list, info)
	}
	return list, nil
}

func GetInt64(val string) int64 {
	v, err := strconv.ParseInt(val, 10, 64)
	if err != nil {
		return 0
	}
	return v
}

// OrigProtoInfo 原始proto信息
type OrigProtoInfo struct {
	FilePath string
	Option   map[string]string
	Service  []*proto.Service
	Rpc      []*proto.RPC
	Message  []*proto.Message
	Import   []string
	ImportAs string
}

// ParseProtoInfo 解析后的proto数据
type ParseProtoInfo struct {
	ProtoFile string            // proto文件url
	Service   *ParseServiceInfo // service信息
	ApiList   []*ParseRpcInfo   // 接口信息
	Option    *ParseOptionInfo  // option信息
	GenData   *ParseGenData     // gen相关数据
}

type ParseGenData struct {
	HandlerFilePath     string // handler文件地址
	HandlerRelativePath string // handler相对目录
	HandlerImportUrl    string // handler引用地址
	HandlerImportAs     string // handler引用地址别名
}

// ParseRpcInfo 解析后的接口信息的信息
type ParseRpcInfo struct {
	ServiceName      string      // service名称
	ServiceDesc      string      // service描述
	Name             string      // 接口名称
	Desc             string      // 接口描述
	Method           string      // 接口方式
	URL              string      // 接口路径
	Request          *Message    // 接口入参
	Response         *Message    // 接口出参
	Middlewares      []string    // 接口中间件
	Roles            []string    // 接口权限组
	Output           string      // 接口出参
	Limiter          LimitOption // 接口限流器
	CtlImport        string      // handler的import引用
	CtlImportAs      string      // handler的import引用别名
	MiddlewareString string      // 接口中间件
	RoleString       string      // 接口权限组
	LimiterString    string      // 接口限流器
}

type ParseServiceInfo struct {
	Name string
}

type ParseOptionInfo struct {
	GoPackage       string
	ServiceNodeName string
	HandlerFile     string
	PbImportAs      string
}

type LimitOption struct {
	Type         string // 限流器类型
	Mode         string // 限流模式
	Cap          int64  // 容量数
	IntervalTime int64  // 令牌间隔时间(毫秒)
	ExpireTime   int64  // 令牌过期时间(毫秒)
}

type Result struct {
	ErrList      []string // 错误列表
	ServiceCount int      // service数量
	ApiCount     int      // 接口总数量
	NewAddCount  int      // 新建数量
}

// Message proto定义的message信息
type Message struct {
	Name string
}
